package market_insight

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"git.mycaigou.com/gfyx/common/cache"
	dto "git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/dto/business_opportunity"
	"git.mycaigou.com/gfyx/micro-gfyx-api/infrastructure/repository/bigdata"
	"git.myscrm.cn/golang/stark/v4"
	"strconv"
	"strings"
	"time"
)

const marketInsightCacheKeyPrefix = "market_insight"

type marketInsightService struct {
	bigdataRepository bigdata.BigdataRepositoryIface
}

func NewMarketInsightService(
	bigdataRepository bigdata.BigdataRepositoryIface,
) MarketInsightServiceIface {
	return &marketInsightService{
		bigdataRepository: bigdataRepository,
	}
}

func (s *marketInsightService) GetProjectHistogram(ctx context.Context, request *dto.GetProjectHistogramRequest) ([]*dto.HistogramItem, error) {
	if request == nil || request.ProjectType == "" {
		return nil, errors.New("GetProjectHistogram 参数错误")
	}

	resp := make([]*dto.HistogramItem, 0)
	// 读缓存
	cacheRedis, _ := cache.Redis()
	cacheKey := fmt.Sprintf("%s.histogram.%s.%s.%s", marketInsightCacheKeyPrefix,
		request.ProjectType,                       //项目类型，land，build，sale
		request.ProvinceCode+"_"+request.CityCode, //省份+城市code
		strings.Split(request.BeginDate, " ")[0]+"_"+strings.Split(request.EndDate, " ")[0]) //日期，只取到年月日
	cacheData, err := cacheRedis.Get(ctx, cacheKey)
	if err != nil {
		return nil, err
	}
	if cacheData != "" {
		err = json.Unmarshal([]byte(cacheData), &resp)
		if err == nil {
			return resp, nil
		}
	}

	switch request.ProjectType {
	case "land":
		result, err := s.bigdataRepository.GetLandHistogram(ctx, &bigdata.MarketInsightCommonRequest{
			TimeBegin:    request.BeginDate,
			TimeEnd:      request.EndDate,
			ProvinceCode: request.ProvinceCode,
			CityCode:     request.CityCode,
		})
		if err != nil {
			return nil, err
		}
		if result != nil {
			for _, item := range result.List {
				area, _ := strconv.ParseFloat(item.AllArea, 64)
				resp = append(resp, &dto.HistogramItem{
					Area:   area,
					Name:   item.LandUsage,
					Number: item.Number,
				})
			}
		}
	case "build":
		result, err := s.bigdataRepository.GetBuildHistogram(ctx, &bigdata.MarketInsightCommonRequest{
			TimeBegin:    request.BeginDate,
			TimeEnd:      request.EndDate,
			ProvinceCode: request.ProvinceCode,
			CityCode:     request.CityCode,
		})
		if err != nil {
			return nil, err
		}
		if result != nil {
			for _, item := range result.List {
				resp = append(resp, &dto.HistogramItem{
					Area:   item.AllArea,
					Name:   item.PrjType,
					Number: item.Number,
				})
			}
		}
	case "sale":
		result, err := s.bigdataRepository.GetSaleHistogram(ctx, &bigdata.MarketInsightCommonRequest{
			TimeBegin:    request.BeginDate,
			TimeEnd:      request.EndDate,
			ProvinceCode: request.ProvinceCode,
			CityCode:     request.CityCode,
		})
		if err != nil {
			return nil, err
		}
		if result != nil {
			for _, item := range result.List {
				area, _ := strconv.ParseFloat(item.BuildArea, 64)
				resp = append(resp, &dto.HistogramItem{
					Area:   area,
					Name:   item.FitmentFee,
					Number: item.Number,
				})
			}
		}
	}

	//单位转换，平米转公顷,保留2位小数 1公顷=10000平米
	for _, item := range resp {
		item.Area, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", item.Area/10000), 64)
	}

	cacheTime := getCacheTime(ctx)
	respData, _ := json.Marshal(resp)
	_, _ = cacheRedis.Set(ctx, cacheKey, string(respData), true, cacheTime)
	return resp, nil
}

func (s *marketInsightService) GetProjectTop20Company(ctx context.Context, request *dto.GetProjectHistogramRequest) ([]*dto.ProjectTopListItem, error) {
	if request == nil || request.ProjectType == "" {
		return nil, errors.New("GetProjectTop20Company 参数错误")
	}

	resp := make([]*dto.ProjectTopListItem, 0)
	// 读缓存
	cacheRedis, _ := cache.Redis()
	cacheKey := fmt.Sprintf("%s.top20company.%s.%s.%s", marketInsightCacheKeyPrefix,
		request.ProjectType,                       //项目类型，land，build，sale
		request.ProvinceCode+"_"+request.CityCode, //省份+城市code
		strings.Split(request.BeginDate, " ")[0]+"_"+strings.Split(request.EndDate, " ")[0]) //日期，只取到年月日
	cacheData, err := cacheRedis.Get(ctx, cacheKey)
	if err != nil {
		return nil, err
	}
	if cacheData != "" {
		err = json.Unmarshal([]byte(cacheData), &resp)
		if err == nil {
			return resp, nil
		}
	}

	var result *bigdata.ProjectTopListData
	bigdataReq := &bigdata.MarketInsightTopListRequest{
		TimeBegin:    request.BeginDate,
		TimeEnd:      request.EndDate,
		ProvinceCode: request.ProvinceCode,
		CityCode:     request.CityCode,
		Page:         1,
		PageSize:     20,
	}
	switch request.ProjectType {
	case "land":
		result, err = s.bigdataRepository.GetLandTopCompany(ctx, bigdataReq)
	case "sale":
		result, err = s.bigdataRepository.GetSaleTopCompany(ctx, bigdataReq)
	case "build":
		result, err = s.bigdataRepository.GetBuildTopCompany(ctx, bigdataReq)
	default:
		err = fmt.Errorf("项目类型错误")
	}
	if err != nil {
		return nil, err
	}
	if result != nil {
		for _, item := range result.List {
			var area float64
			switch item.AllArea.(type) {
			case string:
				area, _ = strconv.ParseFloat(item.AllArea.(string), 64)
			case float64:
				area = item.AllArea.(float64)
			default:
				fmt.Printf("item.AllArea:%#v\n", item.AllArea)
			}

			resp = append(resp, &dto.ProjectTopListItem{
				CompanyName:    item.DeveloperName,
				OrganizingCode: item.OrganizingCode,
				Number:         item.Number,
				Area:           area,
			})
		}
	}
	cacheTime := getCacheTime(ctx)
	respData, _ := json.Marshal(resp)
	_, _ = cacheRedis.Set(ctx, cacheKey, string(respData), true, cacheTime)
	return resp, nil
}

func (s *marketInsightService) GetBidTop20Company(ctx context.Context, request *dto.MarketInsightCommonRequest) ([]*dto.BidTop20CompanyItem, error) {
	resp := make([]*dto.BidTop20CompanyItem, 0)

	data, err := s.bigdataRepository.GetBidTopCompany(ctx, &bigdata.MarketInsightTopListRequest{
		TimeBegin:    request.BeginDate,
		TimeEnd:      request.EndDate,
		ProvinceCode: request.ProvinceCode,
		CityCode:     request.CityCode,
		Page:         1,
		PageSize:     20,
	})
	if err != nil {
		return nil, err
	}

	if data != nil {
		for _, item := range data.List {
			resp = append(resp, &dto.BidTop20CompanyItem{
				CompanyName:    item.CompanyName,
				OrganizingCode: item.OrganizingCode,
				BidNumber:      item.BidCnt,
				WinbidNumber:   item.WinbidCnt,
			})
		}
	}

	return resp, nil
}

func (s *marketInsightService) LineChart(ctx context.Context, request *dto.MarketInsightCommonRequest) (*dto.LineChartResponse, error) {
	var resp dto.LineChartResponse
	endDateTime, err := time.Parse("2006-01-02 15:04:05", request.EndDate)
	if err != nil {
		return nil, errors.New("筛选时间结束时间格式错误")

	}
	//开始时间用结束时间往前推1年
	request.BeginDate = endDateTime.AddDate(-1, 0, 0).Format("2006-01") + "-01 00:00:00"

	cacheRedis, _ := cache.Redis()
	cacheKey := fmt.Sprintf("%s.line-chart.%s.%s", marketInsightCacheKeyPrefix,
		request.ProvinceCode+"_"+request.CityCode,                                           //省份+城市code
		strings.Split(request.BeginDate, " ")[0]+"_"+strings.Split(request.EndDate, " ")[0]) //日期，只取到年月日
	cacheData, err := cacheRedis.Get(ctx, cacheKey)
	if err != nil {
		return nil, err
	}
	if cacheData != "" {
		err = json.Unmarshal([]byte(cacheData), &resp)
		if err == nil {
			return &resp, nil
		}
	}

	//数组初始化空切片，保障返回值非null
	resp.Land.Detail = make([]*dto.LineChartDetail, 0)
	resp.Build.Detail = make([]*dto.LineChartDetail, 0)
	resp.Sale.Detail = make([]*dto.LineChartDetail, 0)
	resp.Bid.Detail = make([]*dto.LineChartBidDetail, 0)

	data, err := s.bigdataRepository.GetLineChart(ctx, &bigdata.MarketInsightCommonRequest{
		TimeBegin:    request.BeginDate,
		TimeEnd:      request.EndDate,
		ProvinceCode: request.ProvinceCode,
		CityCode:     request.CityCode,
	})
	if err != nil {
		return nil, err
	}
	if data == nil {
		return &resp, nil
	}
	endDateTime, _ = time.Parse("2006-01-02 15:04:05", endDateTime.Format("2006-01")+"-01 00:00:00")
	for i := 1; i <= 12; i++ {
		// 12个月，每个月都需要有一个数据
		month := endDateTime.AddDate(0, i-12, 0).Format("2006-01")
		hasMonthData := false

		for _, item := range data.List {
			if item.Month == month {
				hasMonthData = true
				resp.Land.Number += item.TakeLandCnt
				resp.Land.Detail = append(resp.Land.Detail, &dto.LineChartDetail{
					Area:   item.TakeLandAreaCnt,
					Month:  item.Month,
					Number: item.TakeLandCnt,
				})
				resp.Build.Number += item.ProjectCnt
				resp.Build.Detail = append(resp.Build.Detail, &dto.LineChartDetail{
					Area:   item.ProjectAreaCnt,
					Month:  item.Month,
					Number: item.ProjectCnt,
				})
				resp.Sale.Number += item.OpeningCnt
				resp.Sale.Detail = append(resp.Sale.Detail, &dto.LineChartDetail{
					Area:   item.BuildAreaCnt,
					Month:  item.Month,
					Number: item.OpeningCnt,
				})
				resp.Bid.Number += item.BidCnt + item.WinbidCnt
				resp.Bid.Detail = append(resp.Bid.Detail, &dto.LineChartBidDetail{
					BidNumber:    item.BidCnt,
					Month:        item.Month,
					WinbidNumber: item.WinbidCnt,
				})
				break
			}
		}
		if !hasMonthData {
			resp.Land.Detail = append(resp.Land.Detail, &dto.LineChartDetail{
				Area:   0,
				Month:  month,
				Number: 0,
			})
			resp.Build.Detail = append(resp.Build.Detail, &dto.LineChartDetail{
				Area:   0,
				Month:  month,
				Number: 0,
			})
			resp.Sale.Detail = append(resp.Sale.Detail, &dto.LineChartDetail{
				Area:   0,
				Month:  month,
				Number: 0,
			})
			resp.Bid.Detail = append(resp.Bid.Detail, &dto.LineChartBidDetail{
				BidNumber:    0,
				Month:        month,
				WinbidNumber: 0,
			})
		}
	}

	cacheTime := getCacheTime(ctx)
	respData, _ := json.Marshal(resp)
	_, _ = cacheRedis.Set(ctx, cacheKey, string(respData), true, cacheTime)
	return &resp, nil
}

func (s *marketInsightService) BidHistogram(ctx context.Context, request *dto.MarketInsightCommonRequest) (*dto.BidHistogramResponse, error) {
	resp := &dto.BidHistogramResponse{}
	//todo 调用数据服务接口

	data, err := s.bigdataRepository.GetBidHistogram(ctx, &bigdata.MarketInsightCommonRequest{
		TimeBegin:    request.BeginDate,
		TimeEnd:      request.EndDate,
		ProvinceCode: request.ProvinceCode,
		CityCode:     request.CityCode,
	})
	if err != nil {
		return nil, err
	}
	if data != nil && len(data.List) > 0 {
		resp.BidNumber = data.List[0].BidCnt
		resp.WinbidNumber = data.List[0].WinbidCnt
	}

	return resp, nil
}

func (s *marketInsightService) CustomerTop10(ctx context.Context, request *dto.MarketInsightCommonRequest) ([]*dto.CustomerTopItem, error) {
	resp := make([]*dto.CustomerTopItem, 0)
	cacheRedis, _ := cache.Redis()
	cacheKey := fmt.Sprintf("%s.top10customer.%s.%s", marketInsightCacheKeyPrefix,
		request.ProvinceCode+"_"+request.CityCode,                                           //省份+城市code
		strings.Split(request.BeginDate, " ")[0]+"_"+strings.Split(request.EndDate, " ")[0]) //日期，只取到年月日
	cacheData, err := cacheRedis.Get(ctx, cacheKey)
	if err != nil {
		return nil, err
	}
	if cacheData != "" {
		err = json.Unmarshal([]byte(cacheData), &resp)
		if err == nil {
			return resp, nil
		}
	}
	// 调用数据服务接口
	data, err := s.bigdataRepository.GetCustomerTopList(ctx, &bigdata.MarketInsightTopListRequest{
		TimeBegin:    request.BeginDate,
		TimeEnd:      request.EndDate,
		ProvinceCode: request.ProvinceCode,
		CityCode:     request.CityCode,
		Page:         1,
		PageSize:     10,
	})
	if err != nil {
		return nil, err
	}
	if data == nil {
		return resp, nil
	}

	for _, item := range data.List {
		resp = append(resp, &dto.CustomerTopItem{
			CompanyName:    item.CompanyName,
			OrganizingCode: item.OrganizingCode,
			CompanyType:    item.CompanyTypeLabel,
		})
	}
	cacheTime := getCacheTime(ctx)
	respData, _ := json.Marshal(resp)
	_, _ = cacheRedis.Set(ctx, cacheKey, string(respData), true, cacheTime)
	return resp, nil
}

func (s *marketInsightService) CustomerPieChart(ctx context.Context, request *dto.MarketInsightCommonRequest) (*dto.CustomerPieChartResponse, error) {
	resp := &dto.CustomerPieChartResponse{
		Detail: make([]*dto.CustomerPieChartDetail, 0),
	}
	cacheRedis, _ := cache.Redis()
	cacheKey := fmt.Sprintf("%s.customer-pie-chart.%s.%s", marketInsightCacheKeyPrefix,
		request.ProvinceCode+"_"+request.CityCode,                                           //省份+城市code
		strings.Split(request.BeginDate, " ")[0]+"_"+strings.Split(request.EndDate, " ")[0]) //日期，只取到年月日
	cacheData, err := cacheRedis.Get(ctx, cacheKey)
	if err != nil {
		return nil, err
	}
	if cacheData != "" {
		err = json.Unmarshal([]byte(cacheData), &resp)
		if err == nil {
			return resp, nil
		}
	}
	// 调用数据服务接口
	data, err := s.bigdataRepository.GetCustomerPieChart(ctx, &bigdata.MarketInsightCommonRequest{
		TimeBegin:    request.BeginDate,
		TimeEnd:      request.EndDate,
		ProvinceCode: request.ProvinceCode,
		CityCode:     request.CityCode,
	})
	if err != nil {
		return nil, err
	}

	if data == nil {
		return resp, nil
	}

	for _, item := range data.List {
		resp.Number += item.Number
		resp.Detail = append(resp.Detail, &dto.CustomerPieChartDetail{
			CompanyType: item.CompanyTypeLabel,
			Number:      item.Number,
		})
	}
	cacheTime := getCacheTime(ctx)
	respData, _ := json.Marshal(resp)
	_, _ = cacheRedis.Set(ctx, cacheKey, string(respData), true, cacheTime)
	return resp, nil
}

func getCacheTime(ctx context.Context) int {
	var cacheTime = 3600
	//设置缓存，因为数据清洗大概是在8点完成
	//如果当前时间是8点以前，那么久缓存到当今天的8点
	//如果当前时间晚于8点，那么缓存到第二天的早8点
	if time.Now().Hour() < 8 {
		today8am, _ := time.Parse("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" 08:00:00")
		cacheTime = int(today8am.Sub(time.Now()).Seconds())
	} else {
		tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
		tomorrow8am, _ := time.Parse("2006-01-02 15:04:05", tomorrow+" 08:00:00")
		cacheTime = int(tomorrow8am.Sub(time.Now()).Seconds())
	}
	return cacheTime
}

func (s *marketInsightService) ClearMarketInsightCache(ctx context.Context) error {
	cacheRedis, _ := cache.Redis()

	cacheKeys, err := cacheRedis.Keys(ctx, marketInsightCacheKeyPrefix+"*")
	if err != nil {
		return err
	}

	for _, k := range cacheKeys {
		_, err = cacheRedis.Del(ctx, k)
		if err != nil {
			stark.Logger.Errorf(ctx, "ClearMarketInsightCache,del key:%s, err:%s", k, err.Error())
		}
	}

	return nil
}
